/*
 *
 *  * Copyright (c) 2016. For Intelligent Group.
 *
 */

package com.intelligent.ispc.chart.spc;


import com.google.common.collect.Lists;
import com.intelligent.ispc.common.utils.MathUtils;
import com.intelligent.ispc.core.dto.ChartDto;
import com.intelligent.ispc.foundation.chart.AbstractChart;
import com.intelligent.ispc.foundation.chart.axis.FixedNumberAxis;
import com.intelligent.ispc.foundation.chart.util.ChartColor;
import com.intelligent.ispc.foundation.chart.util.ChartUtil;
import com.intelligent.ispc.r.dto.ControlDataDto;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.labels.StandardXYItemLabelGenerator;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.ValueMarker;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.Range;
import org.jfree.data.xy.IntervalXYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.RectangleInsets;

import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.List;

/**
 * Created by cherry on 2016/8/4.
 * Modified by QiangChen on 2016/11/7.
 */
public class ControlChart extends AbstractChart {

    private IntervalXYDataset dataset;
    private ControlDataDto sourceData;
    private JFreeChart localChart;
    private boolean isDataValid;
    private ChartDto chart;

    /**
     * @param controlDataDto is the original data of the drawing.
     * @param chart          is used to record the value of the slider so that there is a slider line in the exported image.
     */
    public ControlChart(ControlDataDto controlDataDto, ChartDto chart) {
        isDataValid = true;
        this.sourceData = controlDataDto;
        this.chart = chart;
        dataset = createDataset();
    }

    @Override
    protected JFreeChart buildChart() {
        //Blank panel is returned when drawing data is invalid.
        if (!isDataValid) {
            return this.blankChart(chart.getName());
        }

        localChart = ChartFactory.createXYLineChart(sourceData.getControlChartType(), "", "", dataset, PlotOrientation.VERTICAL, false, true, false);

        XYPlot localXYPlot = (XYPlot) localChart.getPlot();

        XYLineAndShapeRenderer localLineAndShapeRenderer = (XYLineAndShapeRenderer) localXYPlot.getRenderer();
        localLineAndShapeRenderer.setSeriesPaint(0, ChartColor.RED);
        localLineAndShapeRenderer.setBaseShapesVisible(true);
        localLineAndShapeRenderer.setDrawOutlines(true);
        localLineAndShapeRenderer.setUseFillPaint(true);
        localLineAndShapeRenderer.setBaseFillPaint(ChartColor.RED);
        localLineAndShapeRenderer.setSeriesStroke(0, new BasicStroke(1.0F));
        localLineAndShapeRenderer.setSeriesOutlineStroke(0, new BasicStroke(1.0F));
        final Ellipse2D ellipse2D = new Ellipse2D.Double(-2.0D, -2.0D, 4.0D, 4.0D);
        localLineAndShapeRenderer.setSeriesShape(0, ellipse2D);
        localLineAndShapeRenderer.setBaseItemLabelGenerator(new StandardXYItemLabelGenerator());
        localLineAndShapeRenderer.setBaseItemLabelsVisible(false);
        NumberFormat nf = NumberFormat.getInstance();
        nf.setMaximumFractionDigits(this.getDigNum());
        nf.setGroupingUsed(false);
        StandardXYToolTipGenerator generator = new StandardXYToolTipGenerator("{0}: ({1}, {2})", NumberFormat.getInstance(), new DecimalFormat(this.getDigNumFormat()));
        localLineAndShapeRenderer.setBaseToolTipGenerator(generator);

        FixedNumberAxis axis = new FixedNumberAxis();
        axis.setNumberFormatOverride(nf);

        List<Double> data = Lists.newArrayList();

        for (int i = 0; i < sourceData.getMeanData().length; i++) {
            if (!Double.isNaN(sourceData.getMeanData()[i])) {
                data.add(sourceData.getMeanData()[i]);
            }
        }

        for (int j = 0; j < sourceData.getY().length; j++) {
            if (!Double.isNaN(sourceData.getY()[j])) {
                data.add(sourceData.getY()[j]);
            }
        }
        //The maximum and minimum values are calculated to limit the range of Y coordinates.
        double min = 0;
        double max = 0;
        if (data != null && data.size() != 0) {
            min = MathUtils.findMin(data);
            max = MathUtils.findMax(data);
        }
        //Define the range of Y coordinates.
        if (max != min) {
            final double factor = 0.20;
            double reserve = (max - min) * factor;
            axis.setR(new Range(min - reserve, max + reserve));
            axis.setTickLabelInsets(new RectangleInsets(15, 0, 15, 0));
            localXYPlot.setRangeAxis(axis);
        }

        FixedNumberAxis domainAxis = new FixedNumberAxis();
        domainAxis.setR(localXYPlot.getDomainAxis().getRange());
        localXYPlot.setDomainAxis(domainAxis);

        //Draw lcl、-2σ、-σ、c、σ、2σ、ucl line.
        double[] meanData = sourceData.getMeanData();
        Color[] colors = new Color[]{ChartColor.RED, ChartColor.BLUE, ChartColor.GREEN, ChartColor.GRAYBLUE, ChartColor.GREEN, ChartColor.BLUE, ChartColor.RED};
        String[] lineTitles = new String[]{"lcl=", "-2σ=", "-σ=", "c=", "σ=", "2σ=", "ucl="};
        ValueMarker valuemarker;
        for (int i = 0; i < meanData.length; i++) {
            valuemarker = ChartUtil.drawWarningLines(meanData[i], lineTitles[i] + new DecimalFormat(this.getDigNumFormat()).format(meanData[i]), colors[i]);
            valuemarker.setStroke(new BasicStroke(1.0F));
            localXYPlot.addRangeMarker(valuemarker);
        }

        //Draw usl line
        if (this.sourceData.getUsl() != null) {
            double usl = this.sourceData.getUsl();
            valuemarker = ChartUtil.drawWarningLines(usl, "usl=" + new DecimalFormat(this.getDigNumFormat()).format(usl), ChartColor.MAGENTA);
            valuemarker.setStroke(new BasicStroke(1.0F));
            localXYPlot.addRangeMarker(valuemarker);
        }

        //Draw lsl line
        if (this.sourceData.getLsl() != null) {
            double lsl = this.sourceData.getLsl();
            valuemarker = ChartUtil.drawWarningLines(lsl, "lsl=" + new DecimalFormat(this.getDigNumFormat()).format(lsl), ChartColor.MAGENTA);
            valuemarker.setStroke(new BasicStroke(1.0F));
            localXYPlot.addRangeMarker(valuemarker);
        }

        //Draw slider line
        if (sourceData.getGuidesParams() != null && !sourceData.getGuidesParams().isEmpty()) {
            for (int i = 0; i < sourceData.getGuidesParams().size(); i++) {
                if (sourceData.getGuidesParams().get(i) != null) {
                    double scrollValue = sourceData.getGuidesParams().get(i).doubleValue();
                    ValueMarker valueMarker = ChartUtil.drawWarningLines(scrollValue, new DecimalFormat(this.getDigNumFormat()).format(scrollValue), ChartColor.NAVY);
                    valueMarker.setStroke(new BasicStroke(1.0F));
                    localXYPlot.addRangeMarker(valueMarker);
                }
            }
        }
        return localChart;
    }

    /*
    * Create dataset for ControlChart, and the abnormal data is processed as necessary.
    * */
    private IntervalXYDataset createDataset() {
        if (this.getSourceData() == null || this.getSourceData().getX() == null || this.getSourceData().getY() == null || this.getSourceData().getMeanData() == null) {
            isDataValid = false;
        } else {
            ControlDataDto controlDataDto = this.getSourceData();
            double[] x = controlDataDto.getX();
            double[] y = controlDataDto.getY();
            XYSeries localXYSeries = new XYSeries("Control Chart");
            for (int i = 0; i < x.length; i++) {
                if (Double.isNaN(x[i]) || Double.isNaN(y[i])) {
                    continue;
                }
                localXYSeries.add(x[i], y[i]);
            }
            XYSeriesCollection localXYSeriesCollection = new XYSeriesCollection(localXYSeries);
            return localXYSeriesCollection;
        }
        return null;
    }

    public IntervalXYDataset getDataset() {
        return dataset;
    }

    public ControlDataDto getSourceData() {
        return sourceData;
    }

    public ChartDto getChart() {
        return chart;
    }

    public void setChart(ChartDto chart) {
        this.chart = chart;
    }

    public JFreeChart getLocalChart() {
        return localChart;
    }

    public void setLocalChart(JFreeChart localChart) {
        this.localChart = localChart;
    }

    /**
     * Find the minimum value of Y in ControlDataDto.
     *
     * @return the minimum value of Y in ControlDataDto.
     */
    public double getMinY() {
        double[] y = sourceData.getY();
        double min = y[0];
        for (int i = 1; i < y.length; i++) {
            if (y[i] < min) {
                min = y[i];
            }
        }
        return min;
    }

    /**
     * Find the maximum value of Y in ControlDataDto.
     *
     * @return the maximum value of Y in ControlDataDto
     */
    public double getMaxY() {
        double[] y = sourceData.getY();
        double max = y[0];
        for (int i = 1; i < y.length; i++) {
            if (y[i] > max) {
                max = y[i];
            }
        }
        return max;
    }

}
